﻿/*
* Copyright (c) 2010-2012 Tesla Engine Group
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

using System;
using System.IO;

namespace Tesla.Content.Loaders {
    /// <summary>
    /// Resource exporter that writes ISavable types to the Tesla Engine Binary Object (TEBO)
    /// foramt.
    /// </summary>
    public class BinaryExporter : ResourceExporter<ISavable> {
        //TEBO magic word
        private static readonly byte[] MagicWord = new byte[] { 84, 69, 66, 79 };
        //Version number
        private static readonly byte VersionNumber = 1;

        private bool _writeExternals;

        /// <summary>
        /// Gets or sets if the underlying binary savable writer should write
        /// external savables. If saving to a stream, this option will always be
        /// set to false regardless. The default is true.
        /// </summary>
        public bool WriteExternals {
            get {
                return _writeExternals;
            }
            set {
                _writeExternals = value;
            }
        }

        /// <summary>
        /// Creates a new BinaryExporter.
        /// </summary>
        public BinaryExporter()
            : base(".tebo") {
                _writeExternals = true;
        }

        /// <summary>
        /// Saves the specified value. Writing external savables will be disabled.
        /// </summary>
        /// <param name="value">Object to serialize</param>
        /// <param name="output">Stream to serialize the object into</param>
        public override void Save(ISavable value, Stream output) {
            //Write header
            WriteHeader(output);

            //Read in content
            MemoryStream ms = new MemoryStream();
            BinarySavableWriter writer = new BinarySavableWriter(ms);
            writer.WriteSavable<ISavable>(null, value);
            writer.WriteExternals = false;
            writer.Flush();

            //Write content to output stream
            ms.Position = 0;
            output.Write(ms.GetBuffer(), 0, (int) ms.Length);

            writer.Dispose();
        }

        /// <summary>
        /// Saves the specified value. Default external writers will be used if
        /// WriteExternals is true.
        /// </summary>
        /// <param name="value">Object to serialize</param>
        /// <param name="filePath">Full path to the file</param>
        public override void Save(ISavable value, String filePath) {
            Save(value, filePath, null);
        }

        /// <summary>
        /// Saves the specified value. A list of external writers can optionally be passed
        /// to either override the default writers or add new ones.
        /// </summary>
        /// <param name="value">Object to serialize</param>
        /// <param name="filePath">Full path to the file</param>
        /// <param name="writers">External writers to be used with the savable writer.</param>
        public void Save(ISavable value, String filePath, params ExternalWriter[] writers) {
            filePath = Path.ChangeExtension(filePath, Extension);
            FileStream output = File.Create(filePath);

            //Write header
            WriteHeader(output);

            //Read in content
            BinarySavableWriter writer = new BinarySavableWriter(output, filePath);
            writer.WriteExternals = _writeExternals;

            //Register externals, but if we're not writing any don't bother
            if(_writeExternals) {
                if(writers != null || writers.Length > 0) {
                    foreach(ExternalWriter extWr in writers) {
                        writer.RegisterExternalWriter(extWr);
                    }
                }
            }
            writer.WriteSavable<ISavable>(null, value);
            writer.Flush();

            //Dispose of resources
            writer.Dispose();
        }

        private void WriteHeader(Stream output) {
            output.Position = 0;
            output.Write(MagicWord, 0, 4);
            output.WriteByte(VersionNumber);
        }
    }
}
